// 10/03/14
package ie.ucd.ABLE.MachineLearning;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

import weka.core.Instances;

public class CopyOfStepChangeDetection {

	private ArrayList<CurrentCostData> CurrentCostSensorData;
	private MySQLConnect mySQLDB;
	private int DATA_COUNTER;
	private int LIMIT_DATA_LOADING;
	private int THRESH_HOLD_PLUS, THRESH_HOLD;
	private int THRESH_HOLD_MINUS;
	private java.util.Calendar cal;


	public CopyOfStepChangeDetection(){

		DATA_COUNTER = 0;
		LIMIT_DATA_LOADING = 1000000;
		THRESH_HOLD_PLUS = 80;
		THRESH_HOLD_MINUS = -80;
		THRESH_HOLD = 100;
		m_startup=10;
		m_thresholds=new ArrayList<Double>();
		m_statistics=new ArrayList<Double>(); 
		dataStreamIndex=0;
		cps = new ArrayList<Integer>();
		dts = new ArrayList<Integer>();
		CurrentCostSensorData = new ArrayList<CurrentCostData>();
		//set Timezone to GMT
		cal = Calendar.getInstance();
		cal.setTimeZone(TimeZone.getTimeZone("GMT"));
		//		connectWithMysqlServer();



	}

	public void connectWithMysqlServer(){
		//Connect to My SQL Server...
		mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");

		if (mySQLDB.getDbConnection()==null){
			mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");
		}else{
			System.out.println("Connected to MySQL Server...");
		}		
	}

	public void retreiveRecordsAndFillArrayList(String query, String sensorType){
		//to check memory usage of java... debugging in case memory crashes...
		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
		int MEGABYTE = (1024*1024);
		//variable declaration to be used while retreiving mysql data...
		ResultSet resultS;
		double SensedValue; 
		Timestamp SenseDate = null; 
		int Temperature;
		try {		
			resultS = mySQLDB.queryTable(query);
			if(sensorType.equalsIgnoreCase("CurrentCost")){	//if its epiSensor do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SenseDate = resultS.getTimestamp("SenseDate", cal);
						SensedValue = resultS.getInt("SenseValue");
						Temperature = resultS.getInt("temp");
						CurrentCostSensorData.add(new CurrentCostData(SenseDate, 0, (int)SensedValue, Temperature));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					resultS.close(); //close result set now...
					System.out.println(DATA_COUNTER+ " Episensor unlabeled data retrieved...");
				}else{
					System.out.println("No EpiSensor data to be retrieved...");
				}
			}else{
				System.out.println("Nothing retreived...");		//if its neither of above	
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}catch (OutOfMemoryError e){
			MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
			long maxMemory = heapUsage.getMax() / MEGABYTE;
			long usedMemory = heapUsage.getUsed() / MEGABYTE;
			System.out.println(" : Memory Used :" + usedMemory + "M/" + maxMemory + "M");
		}
	}

	public void readFromCSV(){

		try
		{
			FileReader fr = new FileReader("CurrentCostTraining.csv");
			BufferedReader br = new BufferedReader(fr);
			String stringRead = br.readLine();

			while( stringRead != null )
			{
				//				System.out.println(stringRead);
				String[] elements = stringRead.split(",");

				if(elements.length < 3) {
					throw new RuntimeException("line too short"); //handle missing entries
				}

				String dTime = elements[0];
				String sensorValue = elements[1];
				String applianceActive = elements[2];

				CurrentCostSensorData.add(new CurrentCostData(Timestamp.valueOf(dTime),0,0,Integer.valueOf(sensorValue)));

				// read the next line
				stringRead = br.readLine();
			}
			br.close( );
		}
		catch(IOException ioe){
		}
	}



	// ================================= version 001 ================================== //

	public void findStepChangeVersion001(){
		int eventCheck, countEvent=0;

		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {
			eventCheck = CurrentCostSensorData.get(i+1).getSenseValue()-CurrentCostSensorData.get(i).getSenseValue();
			if(eventCheck >= THRESH_HOLD_PLUS || eventCheck < THRESH_HOLD_MINUS){ // check if the there is a gap
				System.out.println("EVENT DETECTED BETWEEN: "+CurrentCostSensorData.get(i).getDateTime()+" AND "+CurrentCostSensorData.get(i+1).getDateTime()+" VALUES: "+eventCheck);
				countEvent++;
			}
		}
		System.out.println(countEvent);
	}



	// ================================= version 002 ================================== //

	public void findStepChangeVersion002(){
		int baseLine, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		boolean eventStarted = false, captureiThIndex = true, eventFinished = false;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 80; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.
		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {
			if(eventStarted==false){
				if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
					eventStartTime = CurrentCostSensorData.get(i).getDateTime();
					eventStarted = true;
					eventFinished = false; // to be used for fixing ithIndex value...
					captureiThIndex = true; // to be used to allow recording ithIndex value...
					baseLine = CurrentCostSensorData.get(i).getSenseValue();
					System.out.println("\nEvent started: "+eventStartTime);
				}else{
					//recalculate baseline here...an average for stable readings 
					if(captureiThIndex){
						if(eventFinished){
							ithIndex = i-1;
						}else{
							ithIndex = i;
						}
						captureiThIndex = false;
					}
					RealPowerSum = 0;
					for (int j = ithIndex; j <= i; j++) {
						RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
					}baseLine = RealPowerSum/(i+1-ithIndex);
				}

			}else{ // what if another event starts after an event already started...?				
				if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
					eventEndTime = CurrentCostSensorData.get(i).getDateTime();
					eventStarted = false;
					captureiThIndex = true;
					eventFinished = true; // to be used for fixing ithIndex value...
					baseLine = CurrentCostSensorData.get(i).getSenseValue();
					eventCount++;
					System.out.println("Event finished: "+eventEndTime);
				}else{
					//recalculate baseline here...an average for steady readings 
					if(captureiThIndex){
						ithIndex = i-1; // because this is in else block and it will execute in next iteration
						captureiThIndex = false;
					}
					RealPowerSum = 0;
					for (int j = ithIndex; j <= i; j++) {
						RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
					}baseLine = RealPowerSum/(i+1-ithIndex);
				}			
			}
		}
		System.out.println(eventCount); // print number of event detected
	}



	// ================================= version 003 ================================== //

	public void findStepChangeVersion003(){
		//inner class which will create a seperate thread for each appliance...
		class NewApplianceThread extends Thread{

			Timestamp eventStartTime, eventEndTime;
			int startIndex;
			int baseLine;
			boolean loopTrue;

			public NewApplianceThread(Timestamp eStartTime, int sIndex, int bLine){				
				eventStartTime = eStartTime;
				startIndex = sIndex;
				baseLine = bLine;
				loopTrue = true; 
			}

			@Override
			public void run() {
				int i = startIndex;
				while(loopTrue){
					if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
						eventEndTime = CurrentCostSensorData.get(i).getDateTime();
						loopTrue=false;
					}
					i++;	
				}
			}

		}


		int baseLine, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 80; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.

		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {


			if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
				eventStartTime = CurrentCostSensorData.get(i).getDateTime();
				System.err.println("\nEvent started: "+eventStartTime+"\n");
				captureNewEvent(eventStartTime, i, baseLine);
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				eventCount++;
			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				System.err.println("\nEvent finished: "+eventEndTime);
			}else{
				//recalculate baseline here...an average for stable readings 
				RealPowerSum = 0;
				for (int j = ithIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-ithIndex);
			}			
		}
		System.out.println(eventCount); // print number of event detected
	}

	public void captureNewEvent(Timestamp eStartTime, int sIndex, int bLine){
		int i = sIndex;
		int baseLine = CurrentCostSensorData.get(i).getSenseValue();
		Timestamp eventEndTime;
		boolean loopTrue = true;
		while(loopTrue){
			if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				loopTrue=false;
			}else{
				//recalculate baseline here...an average for stable readings 
				int RealPowerSum = 0;
				for (int j = sIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-sIndex);
				// for testing...
				int realPowerValue = CurrentCostSensorData.get(i).getSenseValue()-bLine;
				System.out.println("Date: "+CurrentCostSensorData.get(i).getDateTime()+" Value: "+realPowerValue);
			}

			i++;	
		}
	}

	// ================================= version 004 ================================== //
	// something like...   if (t0 >= tValue && !beginFound) { and   if (t0 >= tValue && beginFound) { this is multi appliance... 



	public void findStepChangeVersion004(){
		List<Integer> stepChangeList = new ArrayList<Integer>();
		int baseLine, lastStableBaseLine=0, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		boolean eventStarted = false, calculateBaseLine = true, eventFinished = false;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 75; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.
		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {

			if(CurrentCostSensorData.get(i).getSenseValue()-THRESH_HOLD <= THRESH_HOLD){
				//				stepChangeList.add(THRESH_HOLD);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue());
				calculateBaseLine = true;
				ithIndex = i;
			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
				eventStartTime = CurrentCostSensorData.get(i).getDateTime();
				lastStableBaseLine = baseLine;
				stepChangeList.add(baseLine);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue()-baseLine);

				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				eventCount++;
				System.err.println("\nEvent started: "+eventStartTime+"\n");
				calculateBaseLine = false; 

			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				System.err.println("\nEvent finished: "+eventEndTime);

			}else{
				calculateBaseLine = true; 
				stepChangeList.add(lastStableBaseLine);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue()-lastStableBaseLine);
			}

			if(calculateBaseLine){
				//recalculate baseline here...an average for stable readings 
				RealPowerSum = 0;
				for (int j = ithIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-ithIndex);
			}

			for (int j = 0; j < stepChangeList.size(); j++) {
				System.out.println("Date: "+CurrentCostSensorData.get(i).getDateTime()+" Value: "+stepChangeList.get(j));
			}
			stepChangeList.clear();
		}
		System.out.println(eventCount);
	}


	////////////////////////////////////////////// version005: GLR from cpt ///////////////////////////////////////////////
	private double maxValue;
	private int maxIndex;
	private int m_startup;
	private List<Double> m_thresholds;
	private long dataStreamIndex;
	private List<Double> m_statistics;
	private List<Integer> cps, dts;

	public void findStepChangeVersion005(){
		int dt, cp, i, lastChange=0, size=CurrentCostSensorData.size();
		double threshold=5, temp_obs=0;
		int thresholdLength=m_thresholds.size();
		// startup for 5 readings assuming that an appliance is turned on for at least half a minute.
		m_startup=5; // just in case if we want any startup: number of streams will not be considered for step change detection.
		i=-1;
		while(i+1<size){
			++i;
			// do update things increase size of window by 1 etc.
			dataStreamIndex++;
			temp_obs = (double) CurrentCostSensorData.get(i).getSenseValue();
			if(m_statistics.size()>0){
				//				System.out.println(m_statistics.size());
				temp_obs=m_statistics.get(m_statistics.size()-1).doubleValue() + CurrentCostSensorData.get(i).getSenseValue();
			}
			//			System.out.println(temp_obs);
			m_statistics.add(temp_obs);

			if(dataStreamIndex >= m_startup){ //window size = m_startup
				//				System.out.println(dataStreamIndex);

				findMaxAndIndexOfMax();

				//				if(thresholdLength==0){
				//					threshold=10;
				//				}else if(dataStreamIndex >= thresholdLength){
				//					threshold=m_thresholds.get(m_thresholds.size()-1).doubleValue();
				//				}else{
				//					threshold=m_thresholds.get((int)dataStreamIndex-1).doubleValue();
				//				}

				//change found
				if(maxValue > threshold){
					System.out.println(maxValue);
					dt = i+1;
					cp = lastChange + maxIndex +1;
					lastChange = cp;
					i = cp-1;
					dts.add(dt);
					cps.add(cp);
					m_statistics.clear();
					dataStreamIndex=0;
				}
			}else{
				// do nothing
			}		
		}

		//for testing purposes...
		for (int j = 0; j < dts.size(); j++) {
			System.out.println("DTS: "+dts.get(j).intValue());
		}
		for (int j = 0; j < cps.size(); j++) {
			System.out.println("CPS: "+cps.get(j).intValue());
		}
		System.out.println("DTS: "+dts.size()+"\tCPS: "+cps.size());
		//		for (int j = 0; j < m_statistics.size(); j++) {
		//			System.out.println(m_statistics.get(j).doubleValue());
		//		}		
	}

	public void findMaxAndIndexOfMax(){
		int size;
		double n0, n1, s0, s1, K, temp;
		List<Double> stepChangeList = new ArrayList<Double>();
		size = m_statistics.size();

		for (int i = 0; i < m_statistics.size()-1; i++) {
			n0 = (double) i+1;
			n1 = (double) size-n0;
			s0 = (double) m_statistics.get(i).doubleValue();
			s1 = (double) m_statistics.get(m_statistics.size()-1).doubleValue() - s0;
			K = size*Math.log((double)size) - n0*Math.log((double)n0)-n1*Math.log((double)n1);
			temp = -size*Math.log(s0+s1) + n0*Math.log(s0) + n1*Math.log(s1)+K;
			stepChangeList.add(-2*temp); //why -2* ?
			if(-2*temp>5) {  //testing
				System.out.println("n0: "+n0+"\t n1: "+n1+"\t s0: "+s0+"\t s1: "+s1+"\t K: "+K+"\t temp: "+temp+"\t -2*temp: "+(-2*temp));
			}
		}
//		stepChangeList.add(0.0);
		//		System.out.println("Size of Array is: "+stepChangeList.size());

		maxIndex=0;
		maxValue=0;
		for (int i = 0; i < stepChangeList.size(); i++) {
			if(stepChangeList.get(i).doubleValue()>maxValue){
				maxValue = stepChangeList.get(i).doubleValue();
				maxIndex = i;
			}
		}
	}

	public static void main(String[] args) {
		CopyOfStepChangeDetection test = new CopyOfStepChangeDetection();
		test.readFromCSV();
		//		System.out.println(test.CurrentCostSensorData.size());
		//				test.findStepChangeVersion001();
		//		test.findStepChangeVersion002();
		test.findStepChangeVersion005();
	}

}
